
const dgram = require('dgram');
const crc = require("./class/Crc");
const { SERVER_IP, SERVER_PORT } = require("./config");
const { start_data_push, stop_data_push } = require("./class/operations");


const server = dgram.createSocket('udp4');
let heart_timer = null;

//  命令应答字典表
const cmd_ack_map = [
    {
        name: "设置积分时间",
        cmd: /20010004/,
        ack: "2000ff00",
    },
    {
        name: "读取积分时间",
        cmd: /20020000/,
        ack: `20000004${Number(1000).toString(16).padStart(8,"0")}`
    },
    {
        name: "读取gain值",
        cmd: /33040001/,
        ack: "33000000"
    },
    {
        name: "读取offset值",
        cmd: /34040000/,
        ack: "34000000"
    },
    {
        name: "读取是否启用外部线路触发",
        cmd: /51020000/,
        ack: "5100000101"
    },
    {
        name: "允许外部线路触发",
        cmd: /51010001/,
        ack: "51000000"
    },
    {
        name: "查询增益校正(是否启用)",
        cmd: /30020000/,
        ack: "3000000101"
    },
    {
        name: "查询偏移校正(是否启用)",
        cmd: /31020000/,
        ack: "3100000101"
    },
    {
        name: "查询基线校正(是否启用)",
        cmd: /32020000/,
        ack: "3200000101"
    },
    {
        name: "启(禁)用增益校正",
        cmd: /30010001/,
        ack: "30000000"
    },
    {
        name: "启(禁)用偏移校正",
        cmd: /31010001/,
        ack: "31000000"
    },
    {
        name: "启(禁)用基线校正",
        cmd: /32010001/,
        ack: "32000000"
    },
    {
        name: "启停采集",
        cmd: /27010001/,
        ack: "27000000",
        operation: (cmd, rinfo) => {
            // console.log("启停采集:", cmd.toString());
            // operation_code  "01":启动采集, "00":结束采集
            let operation_code = cmd.toString().substring(12,14);
            if (operation_code == "01") {
                start_data_push();
            } else {
                stop_data_push();
            }

        }
    },
    {
        name: "启停心跳",
        cmd: /60010001/,
        ack: "60000000",
        operation: (cmd, rinfo) => {
            console.log("启停心跳包:", cmd.toString());
            // operation_code  "01":开启心跳, "00":关闭心跳
            let operation_code = cmd.toString().substring(12,14);
            if (heart_timer != null) { clearInterval(heart_timer); }
            if (operation_code=="01") {
                const data = Buffer.from("bcbcff00000c02bc067a04cf0440016d4f7e2f0de9b3fcfc", 'hex');
                heart_timer = setInterval(() => {
                    server.send(data, 0, data.length, rinfo.port, rinfo.address);
                }, 10000);
            }
        }
    },
    {
        name: "查询小板温度",
        cmd: /7302.{2}00/,
        ack: "7300010000000000000000000000000000000000"    
    }
];


let cmd_str = "";
server.on('message', async function (data, rinfo) {
    cmd_str = data.toString("hex");
    //  正常的命令格式:  bcbc + **(CMD) + **(OPT) + ****(SIZE) + **...**(DATA) + **(CRC) + fcfc
    //  如果命令未以"bcbc"开头则表示接收到错误数据(清空掉)
    if (/^bcbc.*/.test(cmd_str)==false)  { cmd_str = ""; }

    //  接收到正确的命令
    if (/^bcbc.*fcfc$/.test(cmd_str)==true)  {
        const CMD = cmd_str.substr(4, 8);
        console.log("收到命令:", CMD);

        let match_cmd = cmd_ack_map.find(({cmd}) => cmd.test(CMD));
        if (match_cmd == null) {
            console.log("未知命令:", CMD);
            return ;
        }

        let { name, cmd, ack, operation } = match_cmd;
        console.log(`收到[${name}]命令`);

        if (operation instanceof Function) {
            let result = await operation(cmd_str);
            if (result == false) {
                console.log(`处理命令[${name}]过程出错`);
                return ;
            }
        }

        const ack_buffer = Buffer.from("bcbc"+ack+crc.crc32(ack)+"fcfc", 'hex');
        server.send(ack_buffer, 0, ack_buffer.length, rinfo.port, rinfo.address, function(err) {
            if (err) { console.log(`[${name} 应答包发送失败]: ${err.message}`); }
            console.log(`[${name} 应答包发送成功]`);
        });
    }
});
server.on('error', function (err) { console.log(`[server-error]:${err.message}`); });
server.on("listening", function () { console.log(`服务器开始监听, 地址信息为:${SERVER_IP}:${SERVER_PORT}`); });

server.bind(SERVER_PORT, SERVER_IP);
